package org.streets.eis.component.tree;

import java.io.Serializable;
import java.util.Enumeration;

import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.Response;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.html.tree.BaseTree;
import org.apache.wicket.markup.html.tree.DefaultTreeState;
import org.apache.wicket.markup.html.tree.ITreeState;
import org.apache.wicket.markup.html.tree.LinkIconPanel;
import org.apache.wicket.markup.html.tree.LinkTree;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.string.JavascriptUtils;
import org.streets.wicket.markup.tree.CheckBoxTree;
import org.streets.wicket.markup.tree.ICheckableState;

public class TreePanel<T extends Serializable> extends Panel {	
	private static final long serialVersionUID = 1L;
	
	private class State extends DefaultTreeState {
		private static final long serialVersionUID = 1L;
		

		@Override
		public void expandNode(Object node) {
			((Node<?>)node).loadChildren();
			super.expandNode(node);
		}

		@Override
		public boolean isNodeExpanded(Object node) {
			if (((Node<?>)node).getChildCount() == 0) {
				return false;
			} else {
				return super.isNodeExpanded(node);
			}
		}
		
		@Override
		public void selectNode(Object node, boolean selected){
			if (selected){
				super.selectNode(node, selected);
			}
		}
	}	
	
	private boolean preSelectNode=false;
	private boolean showCheckBox;
	private BaseTree tree;
	private transient TreeNode preSelectedNode;

	public TreePanel(String id, DefaultTreeModel treeModel) {
		this(id,treeModel,false);
	}
	
	public TreePanel(String id, DefaultTreeModel treeModel, boolean showCheckBox) {
		super(id);
		this.showCheckBox = showCheckBox;		
		add(tree=createTree(treeModel));
	}
	
	public TreePanel(String id, T rootObject) {
		this(id,rootObject,false);
	}
	
	public TreePanel(String id, T rootObject, boolean showCheckBox) {	
		super(id);
		setRenderBodyOnly(true);
		this.showCheckBox = showCheckBox;
		
		Node<T> rootNode = new Node<T>(rootObject){
			private static final long serialVersionUID = 1L;

			@Override
			protected void load(Node<T> node) {
				TreePanel.this.loadChildNode(node);				
			}	
		};
		
		add(createTree(new DefaultTreeModel(rootNode)));
	}

	@Override
	public void onBeforeRender(){
		TreeNode node = getDefaultExpandedNode();
		if (preSelectNode){
			preSelectedNode = node;
		}
		while (node!=null){
			tree.getTreeState().expandNode(node);
			node = node.getParent();
		}
		
		super.onBeforeRender();		
	}
	
	@Override
	public void onAfterRender(){
		super.onAfterRender();
		
		if (preSelectedNode!=null){			
			Component link = ( (MarkupContainer) tree.getNodeComponent(preSelectedNode)).get("nodeComponent:contentLink");		
			if (link!=null){
				String nodeMarkupId = link.getMarkupId();				
				Response response = getResponse();
				CharSequence js = "Wicket.Event.add(window, \"domready\", function(event) {var o = Wicket.$("+nodeMarkupId+");if(o){o.onclick();}});";				
				JavascriptUtils.writeJavascript(response, js,"selectedEvent");
			}
		}		
	}
	
	@SuppressWarnings("unchecked")
	protected Node<T> getDefaultExpandedNode(){
		Node<T> root = (Node<T>) tree.getModelObject().getRoot();
		if (tree.isRootLess() && root.getChildCount() > 0){
			return (Node<T>) root.getChildAt(0); 
		} else {
			return root;
		}
	}
	
	protected String getDefaultExpandedNodeIdSequence(){
		return "";
	}
	
	public boolean isPreSelectNode() {
		return preSelectNode;
	}

	public TreePanel<T> setPreSelectNode(boolean preSelectNode) {
		this.preSelectNode = preSelectNode;
		return this;
	}
	
	protected BaseTree createTree(DefaultTreeModel treeModel){
		if (TreePanel.this.showCheckBox){
			tree = createCheckBoxTree(treeModel);
		} else {
			tree = createLinkTree(treeModel);
		}
		
		tree.setRootLess(false);
		tree.setOutputMarkupId(true);
		return tree;
	}
	
	@SuppressWarnings("unchecked")	
	protected BaseTree createCheckBoxTree(DefaultTreeModel treeModel){
		return new CheckBoxTree("tree",new Model(treeModel)){
			private static final long serialVersionUID = 1L;

			@Override
			protected ITreeState newTreeState() {
				return new State();
			}
			
			@Override
			protected ResourceReference getNodeIcon(Object node){
				return TreePanel.this.getNodeIcon((Node<T>)node);
			}
			
			@Override
			public Label newContent(String id, IModel<?> model, final ICheckCallback callback){
				String nodeText = TreePanel.this.getNodeText((Node<T>)model.getObject());
				return new Label(id, Model.of(nodeText));	
			}
			
			@Override
			protected void onNodeLabelClicked(TreeNode node, CheckBoxTree tree, AjaxRequestTarget target){
				TreePanel.this.beforeNodeLabelClicked((Node<T>)node,tree,target);
				TreePanel.this.onNodeLabelClicked((Node<T>)node,tree,target);
				TreePanel.this.afterNodeLabelClicked((Node<T>)node,tree,target);
			}	
			
			@Override
			protected void onNodeCheckUpdated(TreeNode node, BaseTree tree, AjaxRequestTarget target){
				TreePanel.this.onNodeCheckUpdated((Node<T>)node,tree,target);
			}
		};
	}
	
	@SuppressWarnings("unchecked")	
	protected BaseTree createLinkTree(DefaultTreeModel treeModel){
		return new LinkTree("tree",new Model(treeModel)){
			private static final long serialVersionUID = 1L;

			@Override
			protected ITreeState newTreeState() {
				return new State();
			}
		
			@Override
			protected Component newNodeComponent(String id, IModel<Object> model){
				return new LinkIconPanel(id, model, this){
					private static final long serialVersionUID = 1L;

					@Override
					protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target){
						super.onNodeLinkClicked(node, tree, target);
						TreePanel.this.beforeNodeLabelClicked((Node<T>)node,tree,target);
						TreePanel.this.onNodeLabelClicked((Node<T>)node,tree,target);
						TreePanel.this.afterNodeLabelClicked((Node<T>)node,tree,target);
					}

					@Override
					protected Component newContentComponent(String componentId, BaseTree tree,IModel<Object> model){
						String nodeText = TreePanel.this.getNodeText((Node<T>)model.getObject());
						return new Label(componentId, Model.of(nodeText));						
					}
					
					@Override
					protected ResourceReference getImageResourceReference(BaseTree tree, Object node){
						ResourceReference res = TreePanel.this.getNodeIcon((Node<T>)node);
						if (res!=null){
							return res;
						} else {
							return super.getImageResourceReference(tree, node);
						}
					}
				};
			}	
		};
	}
	
	
	@SuppressWarnings("unchecked")
	public T getNodeObject( TreeNode node ) {
		if (node instanceof Node) {
			return ((Node<T>)node).getObject();
		} 
		return null;
	}
	
	public void deselectTree(BaseTree tree,TreeNode node) {
		Enumeration<?> nodeEnum = node.children();
		while ( nodeEnum.hasMoreElements() ) {
			TreeNode child = (TreeNode)nodeEnum.nextElement();
			tree.getTreeState().selectNode(child,false);
			deselectTree(tree, child);
		}
	}
	
	public BaseTree getTree() {
		return tree;
	}
	
	public ICheckableState getCheckableState(){
		if (tree instanceof CheckBoxTree){
			return ((CheckBoxTree)tree).getCheckState();
		} else {
			return null;
		}
	}
	
	protected void loadChildNode(Node<T> node){		
	}
	
	protected void onNodeCheckUpdated(Node<T> node, BaseTree tree, AjaxRequestTarget target){
	}
	
	protected void beforeNodeLabelClicked(Node<T> node, BaseTree tree, AjaxRequestTarget target){
	}
	
	protected void onNodeLabelClicked(Node<T> node, BaseTree tree, AjaxRequestTarget target){
	}
	
	protected void afterNodeLabelClicked(Node<T> node, BaseTree tree, AjaxRequestTarget target){
	}
	
	protected ResourceReference getNodeIcon(Node<T> node){
		return null;
	}
	
	protected String getNodeText(Node<T> node){
		return node.toString();
	}
	
}
